package org.dreamwork.websocket;

import org.dreamwork.concurrent.Looper;
import org.dreamwork.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * Created by game on 2017/2/16
 */
public class WebSocketManager {
    private static final Logger logger = LoggerFactory.getLogger (WebSocketManager.class);
    private static final Object LOCKER = new byte[0];
    private static final Map<Class<? extends IWebSocketExecutor<?>>, Set<IWebSocketExecutor<?>>> map = new HashMap<> ();
    private static final String LOOP_NAME = "org.dreamwork.websocket.WebSocketManager";
//    private static final ExecutorService service = Executors.newCachedThreadPool ();

    static {
        Looper.create (LOOP_NAME, 16, 16);
    }

    public static Collection<IWebSocketExecutor<?>> getActiveWebSocketExecutor () {
        synchronized (map) {
            Set<IWebSocketExecutor<?>> set = new HashSet<> ();
            for (Set<IWebSocketExecutor<?>> s : map.values ()) {
                set.addAll (s);
            }
            return set;
        }
    }

    public static void add (IWebSocketExecutor<?> socket) {
        synchronized (LOCKER) {
            if (socket == null) {
                throw new NullPointerException ();
            }

            @SuppressWarnings ("unchecked")
            Class<? extends IWebSocketExecutor<?>> type = (Class<? extends IWebSocketExecutor<?>>) socket.getClass ();
            Set<IWebSocketExecutor<?>> set = map.computeIfAbsent (type, k -> new HashSet<> ());
            set.add (socket);
        }

        if (logger.isTraceEnabled ()) {
            logger.trace ("a client added: " + socket);
        }
    }

    public static void remove (IWebSocketExecutor<?> socket) {
        synchronized (LOCKER) {
            if (socket == null) {
                return;
            }

            @SuppressWarnings ("unchecked")
            Class<? extends IWebSocketExecutor<?>> type = (Class<? extends IWebSocketExecutor<?>>) socket.getClass ();
            Set<IWebSocketExecutor<?>> set = map.get (type);
            if (set != null) {
                set.remove (socket);
            }
        }

        if (logger.isTraceEnabled ()) {
            logger.trace ("a client removed: " + socket);
        }
    }

    @SuppressWarnings ("unchecked")
    private static <T> Set<IWebSocketExecutor<T>> copy (Class<? extends IWebSocketExecutor<T>> type) {
        synchronized (LOCKER) {
            Set set = map.get (type);
            if (set == null) {
                return null;
            }

            return new HashSet<> (set);
        }
    }

    public static<T> void fireDataChanged (Class<? extends IWebSocketExecutor<T>> type, T message) {
        if (type == null || message == null) {
            throw new NullPointerException ();
        }

        Set<IWebSocketExecutor<T>> copy = copy (type);
        if (copy != null) {
            for (IWebSocketExecutor<T> client : copy) {
                try {
                    if (client.matches (message)) {
                        client.send (message);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace ();
                }
            }
        }
    }

    public static<T> void fireDataChanged (Class<? extends IWebSocketExecutor<T>> type, String id, T message) {
        if (StringUtil.isEmpty (id)) {
            fireDataChanged (type, message);
        }

        Set<IWebSocketExecutor<T>> copy = copy (type);
        if (copy != null) {
            for (IWebSocketExecutor<T> client : copy) {
                try {
                    if (client.matches (id, message)) {
                        client.send (message);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace ();
                }
            }
        }
    }

    protected static void run (IWebSocketExecutor<?> executor) {
//        service.execute (executor);
        Looper.runInLoop (LOOP_NAME, executor);
    }

    public static void shutdown () {
        Looper.destory (LOOP_NAME);
/*
        List<Runnable> list = service.shutdownNow ();
        if (!list.isEmpty ()) {
            for (Runnable executor : list) {
                ((IWebSocketExecutor<?>) executor).close ();
            }
        }
*/
    }
}